package GA;

import java.util.Random;

public class Chromosome implements GA_Param, Cloneable {
    private int length;
    private int[] gene;
    private int fitness;
    private int objValue;

    /**
     * Constructor
     *
     * @param length: The length of Chromosome's gene.
     */
    public Chromosome(int length) {
        this.length = length;
        if (length < 0) {
            System.out.println("The length of array is unreasonable.");
        } else {
            this.gene = new int[length];
        }
    }

    /**
     * Deep Clone!!!
     */
    @Override
    public Chromosome clone() {
        Chromosome c = null;
        try {
            c = (Chromosome) super.clone();
            c.setGene(c.getGene().clone());
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
        return c;
    }

    /**
     * Randomly set genes on current chromosome.
     */
    public void randomSetGene() {
        Random random = new Random();
        for (int i = 0; i < length; ++i) {
            gene[i] = Math.round(random.nextFloat());
        }
    }

    /**
     * Set primary genes on current chromosome.
     *
     * @param primaryGeneArray:
     */
    public void setPrimaryGene(int[] primaryGeneArray) {
        if (length >= 0) System.arraycopy(primaryGeneArray, 0, gene, 0, length);
    }

    /**
     *  * Return the fitness of Chromosome.
     *
     * @return int: The chromosome's fitness.
     */
    public int getFitness() {
        return this.fitness;
    }

    /**
     *  * Set the fitness of Chromosome.
     *
     * @param fitness:
     */
    public void setFitness(int fitness) {
        this.fitness = fitness;
    }

    /**
     * Acquire objValue.
     *
     * @return int: The objective value of chromosome.
     */
    public int getObjValue() {
        return objValue;
    }

    /**
     * Set the chromosome's objValue.
     *
     * @param objValue:
     */
    public void setObjValue(int objValue) {
        this.objValue = objValue;
    }

    /**
     * Acquire genes.
     *
     * @return int[]: The array of gene.
     */
    public int[] getGene() {
        return this.gene;
    }

    /**
     * Set the chromosome's gene.
     *
     * @param gene:
     */
    public void setGene(int[] gene) {
        this.gene = gene;
    }

    public boolean isLegal() {
        int row_weight = 0;
        for (int row = 0; row < ATTRIBUTE_ARRAY.length; ++row) {
            for (int j = 0; j < length; ++j) {
                if (gene[j] == 1) {
                    row_weight += ATTRIBUTE_ARRAY[row][j];
                }
            }
            // If the current total attribute is too large
            if (row_weight > LIMIT_ARRAY[row]) {
                return false;
            }
        }
        return true;
    }

    /**
     * Repair the chromosome individual.
     */
    public void repair() {
        int row_weight = 0;
        for (int row = 0; row < ATTRIBUTE_ARRAY.length; ++row) {
            for (int j = 0; j < length; ++j) {
                if (gene[j] == 1) {
                    row_weight += ATTRIBUTE_ARRAY[row][j];
                }
            }
            // If the current total attribute is too large
            if (row_weight > LIMIT_ARRAY[row]) {
                double[] value_rate = new double[length];
                double minValueRate = 1000000;
                int record = 0;
                // Calculate cost performance.
                for (int j = 0; j < ATTRIBUTE_ARRAY[row].length; ++j) {
                    if (gene[j] == 1) {
                        if (ATTRIBUTE_ARRAY[row][j] == 0) {
                            value_rate[j] = 1000000;
                        } else {
                            value_rate[j] = VALUE_ARRAY[j] / ATTRIBUTE_ARRAY[row][j];
                        }
                    } else {
                        value_rate[j] = 1000000;
                    }
                }
                while (row_weight > LIMIT_ARRAY[row]) {
                    for (int m = 0; m < length; ++m) {
                        if (value_rate[m] < minValueRate) {
                            minValueRate = value_rate[m];
                            record = m;
                        }
                    }
                    gene[record] = 0;
                    value_rate[record] = 1000000;
                    minValueRate = 1000000;
                    row_weight -= ATTRIBUTE_ARRAY[row][record];
                }
            }
            row_weight = 0;
        }
    }

    /**
     *  * Calculate the value of Chromosome.
     *
     * @return int: The chromosome's objective value.
     */
    public int calcObjValue() {
        int value = 0;
        for (int i = 0; i < length; ++i) {
            value += (gene[i] * VALUE_ARRAY[i]);
        }
        setObjValue(value);
        return value;
    }

    /**
     *  * Carrying out mutation on the current chromosome.
     *
     * @param rate: the rate of mutation.
     * @return void:
     */
    public void mutate(double rate) {
        Random random = new Random();
        for (int i = 0; i < length; i++) {
            if (random.nextDouble() < rate) {
                gene[i] = 1 - gene[i];
            }
        }
    }

    /**
     * Carrying out mutation on the current chromosome and
     *
     * @param rate: Mutation rate at Different Positions.
     */
    public void mutate(float[] rate) {
        Random random = new Random();
        for (int i = 0; i < length; i++) {
            if (random.nextFloat() < rate[i]) {
                gene[i] = 1 - gene[i];
            }
        }
    }

    /**
     * Print the gene.
     */
    public void printGene() {
        for (int elem : gene) {
            System.out.print(elem + " ");
        }
    }
}
